﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using FrameDisplay.Services;

namespace FrameDisplayClient.UI.ScreenSender
{

    public abstract class ScreenSenderWorker
    {
        private IFrameDisplayService frameDisplayService;
        private string accessCode;

        private Thread thread;
        private int delayInMilliseconds = 1000;
        private bool stop = false;

        // TODO: Is the byte buffer necessary? Seems like image saving doesn't work without a pre-allocated buffer for memory stream.
        private byte[] buffer = new byte[10000000];


        public ScreenSenderWorker(IFrameDisplayService frameDisplayService, string accessCode)
        {
            this.frameDisplayService = frameDisplayService;
            this.accessCode = accessCode;
        }


        public void SendOnceAsync()
        {
            ThreadPool.QueueUserWorkItem(
                delegate (object state) {
                    this.SendOnce();
                });
        }

        private void SendOnce()
        {
            Image capturedImage = this.CaptureImage();

            if (capturedImage != null)
            {
                MemoryStream imageStream = new MemoryStream(this.buffer);

                // TODO: Consider supporting JPEG image sending.

                //ImageCodecInfo jpegCodecInfo = this.GetImageCodecInfo("image/jpeg");
                //EncoderParameters encoderParameters = new EncoderParameters(1);
                //encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 80);
                //capturedImage.Save(imageStream, jpegCodecInfo, encoderParameters);

                capturedImage.Save(imageStream, ImageFormat.Png);
                imageStream.Position = 0;

                this.frameDisplayService.ShowImageFile(imageStream, accessCode);
            }
        }

        private ImageCodecInfo GetImageCodecInfo(string mimeType)
        {
            ImageCodecInfo[] codecInfoList = ImageCodecInfo.GetImageEncoders();

            for (int co = 0; co < codecInfoList.Length; co++)
            {
                if (codecInfoList[co].MimeType == mimeType)
                    return codecInfoList[co];
            }

            return null;
        }


        public void Start()
        {
            if (this.stop)
            {
                throw new Exception("Restarting a stopped worker is not supported.");
            }

            this.thread = new Thread(this.SendingLoop);
            this.thread.IsBackground = true;
            this.thread.Name = "ScreenSenderWorker";
            this.thread.Start();
        }

        public void Stop()
        {
            this.stop = true;
        }

        protected virtual Image CaptureImage()
        {
            Image capturedImage = new Bitmap(300, 100);
            using (Graphics g = Graphics.FromImage(capturedImage))
            using (Font font = new Font(FontFamily.GenericSansSerif, 10))
            {
                g.FillRectangle(Brushes.Black, 0, 0, capturedImage.Width, capturedImage.Height);
                g.DrawString("No source selected.", font, Brushes.White, 10, 10);
            }

            return capturedImage;
        }


        private void SendingLoop()
        {        
            while (!this.stop)
            {
                try
                {
                    this.SendOnce();
                }
                catch
                {
                    // TODO: decide if we should do anything with exception.
                }

                Thread.Sleep(this.delayInMilliseconds);
            }
        }
    }


    public class DisplayScreenSenderWorker : ScreenSenderWorker
    {
        private Screen screen;

        public DisplayScreenSenderWorker(IFrameDisplayService frameDisplayService, string accessCode, Screen screen) :
            base(frameDisplayService, accessCode)
        {
            this.screen = screen;
        }

        protected override Image CaptureImage()
        {
            return SharpScraps.Win32.ScreenCapture.CaptureScreen(this.screen);
        }

    }

    public class WindowScreenSenderWorker : ScreenSenderWorker
    {
        private IntPtr windowHandle;

        public WindowScreenSenderWorker(IFrameDisplayService frameDisplayService, string accessCode, IntPtr windowHandle) :
            base(frameDisplayService, accessCode)
        {
            this.windowHandle = windowHandle;
        }

        protected override Image CaptureImage()
        {
            return SharpScraps.Win32.ScreenCapture.CaptureWindow(this.windowHandle);
        }

    }

}
